package com.zkh.myframe.aspect;

import java.sql.Connection;
import java.util.function.Supplier;

import com.zkh.myutils.database.DatabaseException;
import com.zkh.myutils.database.conutils.PoolUtils;
import com.zkh.myutils.utils.Execute;

/**
 * 自定义事务切面
 * @author zkh
 */
public class TransactionAspect extends AspectSuper {
	//计数，避免切面方法中嵌套切面方法，从而导致事务混乱
	private static ThreadLocal<Integer> lcount = new ThreadLocal<>();
	
	/**
	 * 在新事务中执行一段代码
	 * @param execute 自定义代码
	 */
	public static void runInNewTransaction(Execute execute) {
		runInNewTransaction(()->{
			execute.apply();
			return null;
		});
	}
	
	/**
	 * 在新事务中执行一段代码
	 * @param supplier 自定义代码，并返回一个值
	 */
	public static <T> T runInNewTransaction(Supplier<T> supplier) {
		//获取原事务
		Connection connection = PoolUtils.getTransactionConnection();
		//为空
		if(connection==null) {
			throw new DatabaseException("runInNewTransaction方法必须在事务方法中调用");
		}
		//设置新事务
		PoolUtils.beginNewTransaction(null);
		try {
			//执行自定义方法
			T result = supplier.get();
			//提交事务
			PoolUtils.commit();
			//返回
			return result;
		}catch (Exception e) {
			//回滚事务
			PoolUtils.rollback();
			//抛出异常
			if(e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new DatabaseException(e);
		}finally {
			//还原原事务连接
			PoolUtils.beginNewTransaction(connection);
		}
	}
	
	/**
	 * 前置操作
	 */
	@Override
	public Object before(Object[] args) {
		//未开启事务
		if(lcount.get()==null){
			//初始化
			lcount.set(0);
			//开启事务
			PoolUtils.beginTransaction();
		}
		//记次
		lcount.set(lcount.get() + 1);
		//继续执行
		return true;
	}
	
	/**
	 * 后置操作
	 */
	@Override
	public Object after(Object[] args, Object res) {
		//已结束
		if(lcount.get()==1){
			//移除
			lcount.remove();
			//提交事务
			PoolUtils.commit();
		}
		//记次
		if(lcount.get()!=null){
			lcount.set(lcount.get() - 1);
		}
		//返回当前值
		return res;
	}
	
	/**
	 * 如果执行代理方法发生异常，使用该方法进行捕获
	 */
	@Override
	public Object catchException(Object[] args, Throwable e, Object lastReturnValue) {
		//回滚事务
		if(lcount.get()!=null){
			PoolUtils.rollback();lcount.remove();
		}
		//抛出异常，让调用的方法知道报错了
		if(e instanceof RuntimeException) {
			throw (RuntimeException) e;
		}
		throw new RuntimeException(e);
	}
}
